package com.zhentao.service.impl;

import com.zhentao.service.ParamCacheService;
import com.zhentao.service.ParamDefinitionService;
import com.zhentao.service.ParamValueService;
import com.zhentao.vo.ParamDefinitionVO;
import com.zhentao.vo.ParamValueVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 参数缓存服务实现
 * 
 * @author zhentao
 * @date 2024-12-19
 */
@Slf4j
@Service
public class ParamCacheServiceImpl implements ParamCacheService {

    private static final String PARAM_DEFINITION_PREFIX = "param:def:";
    private static final String PARAM_VALUE_PREFIX = "param:val:";
    private static final String CACHE_STATS_KEY = "param:cache:stats";
    
    // 缓存TTL配置
    private static final long DEFINITION_TTL = 24 * 60 * 60; // 24小时
    private static final long VALUE_TTL = 60 * 60; // 1小时
    private static final long STATS_TTL = 5 * 60; // 5分钟

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private ParamDefinitionService paramDefinitionService;

    @Autowired
    private ParamValueService paramValueService;

    @Override
    public void cacheParamDefinition(String paramKey, ParamDefinitionVO paramDefinition) {
        try {
            String cacheKey = PARAM_DEFINITION_PREFIX + paramKey;
            redisTemplate.opsForValue().set(cacheKey, paramDefinition, DEFINITION_TTL, TimeUnit.SECONDS);
            
            // 更新缓存统计
            updateCacheStats("definition_cache_set", 1);
            
            log.debug("缓存参数定义: {}", paramKey);
        } catch (Exception e) {
            log.error("缓存参数定义失败: {}", paramKey, e);
        }
    }

    @Override
    public ParamDefinitionVO getCachedParamDefinition(String paramKey) {
        try {
            String cacheKey = PARAM_DEFINITION_PREFIX + paramKey;
            Object cached = redisTemplate.opsForValue().get(cacheKey);
            
            if (cached != null) {
                updateCacheStats("definition_cache_hit", 1);
                log.debug("参数定义缓存命中: {}", paramKey);
                return (ParamDefinitionVO) cached;
            } else {
                updateCacheStats("definition_cache_miss", 1);
                log.debug("参数定义缓存未命中: {}", paramKey);
                return null;
            }
        } catch (Exception e) {
            log.error("获取缓存参数定义失败: {}", paramKey, e);
            updateCacheStats("definition_cache_error", 1);
            return null;
        }
    }

    @Override
    public void cacheParamValue(String paramKey, String environment, ParamValueVO paramValue) {
        try {
            String cacheKey = buildValueCacheKey(paramKey, environment);
            redisTemplate.opsForValue().set(cacheKey, paramValue, VALUE_TTL, TimeUnit.SECONDS);
            
            updateCacheStats("value_cache_set", 1);
            log.debug("缓存参数值: {} - {}", paramKey, environment);
        } catch (Exception e) {
            log.error("缓存参数值失败: {} - {}", paramKey, environment, e);
        }
    }

    @Override
    public ParamValueVO getCachedParamValue(String paramKey, String environment) {
        try {
            String cacheKey = buildValueCacheKey(paramKey, environment);
            Object cached = redisTemplate.opsForValue().get(cacheKey);
            
            if (cached != null) {
                updateCacheStats("value_cache_hit", 1);
                log.debug("参数值缓存命中: {} - {}", paramKey, environment);
                return (ParamValueVO) cached;
            } else {
                updateCacheStats("value_cache_miss", 1);
                log.debug("参数值缓存未命中: {} - {}", paramKey, environment);
                return null;
            }
        } catch (Exception e) {
            log.error("获取缓存参数值失败: {} - {}", paramKey, environment, e);
            updateCacheStats("value_cache_error", 1);
            return null;
        }
    }

    @Override
    public void batchCacheParamDefinitions(Map<String, ParamDefinitionVO> paramDefinitions) {
        if (CollectionUtils.isEmpty(paramDefinitions)) {
            return;
        }

        try {
            Map<String, Object> batchData = new HashMap<>();
            for (Map.Entry<String, ParamDefinitionVO> entry : paramDefinitions.entrySet()) {
                String cacheKey = PARAM_DEFINITION_PREFIX + entry.getKey();
                batchData.put(cacheKey, entry.getValue());
            }

            // 批量设置缓存
            redisTemplate.opsForValue().multiSet(batchData);
            
            // 批量设置TTL
            for (String cacheKey : batchData.keySet()) {
                redisTemplate.expire(cacheKey, DEFINITION_TTL, TimeUnit.SECONDS);
            }

            updateCacheStats("definition_batch_cache_set", paramDefinitions.size());
            log.debug("批量缓存参数定义: {} 个", paramDefinitions.size());
        } catch (Exception e) {
            log.error("批量缓存参数定义失败", e);
        }
    }

    @Override
    public Map<String, ParamDefinitionVO> getBatchCachedParamDefinitions(List<String> paramKeys) {
        if (CollectionUtils.isEmpty(paramKeys)) {
            return new HashMap<>();
        }

        try {
            List<String> cacheKeys = paramKeys.stream()
                    .map(key -> PARAM_DEFINITION_PREFIX + key)
                    .collect(Collectors.toList());

            List<Object> cachedValues = redisTemplate.opsForValue().multiGet(cacheKeys);
            
            Map<String, ParamDefinitionVO> result = new HashMap<>();
            for (int i = 0; i < paramKeys.size(); i++) {
                Object cached = cachedValues.get(i);
                if (cached != null) {
                    result.put(paramKeys.get(i), (ParamDefinitionVO) cached);
                }
            }

            int hitCount = result.size();
            int missCount = paramKeys.size() - hitCount;
            
            updateCacheStats("definition_batch_cache_hit", hitCount);
            updateCacheStats("definition_batch_cache_miss", missCount);
            
            log.debug("批量获取参数定义缓存: 请求 {} 个，命中 {} 个", paramKeys.size(), hitCount);
            return result;
        } catch (Exception e) {
            log.error("批量获取缓存参数定义失败", e);
            updateCacheStats("definition_batch_cache_error", paramKeys.size());
            return new HashMap<>();
        }
    }

    @Override
    public void evictParamDefinition(String paramKey) {
        try {
            String cacheKey = PARAM_DEFINITION_PREFIX + paramKey;
            redisTemplate.delete(cacheKey);
            
            updateCacheStats("definition_cache_evict", 1);
            log.debug("清除参数定义缓存: {}", paramKey);
        } catch (Exception e) {
            log.error("清除参数定义缓存失败: {}", paramKey, e);
        }
    }

    @Override
    public void evictParamValue(String paramKey, String environment) {
        try {
            String cacheKey = buildValueCacheKey(paramKey, environment);
            redisTemplate.delete(cacheKey);
            
            updateCacheStats("value_cache_evict", 1);
            log.debug("清除参数值缓存: {} - {}", paramKey, environment);
        } catch (Exception e) {
            log.error("清除参数值缓存失败: {} - {}", paramKey, environment, e);
        }
    }

    @Override
    public void evictAllParamCache(String paramKey) {
        try {
            // 清除参数定义缓存
            evictParamDefinition(paramKey);
            
            // 清除所有环境的参数值缓存
            String valuePattern = PARAM_VALUE_PREFIX + paramKey + ":*";
            Set<String> keys = redisTemplate.keys(valuePattern);
            if (!CollectionUtils.isEmpty(keys)) {
                redisTemplate.delete(keys);
                updateCacheStats("value_cache_evict", keys.size());
            }
            
            log.debug("清除参数所有缓存: {}", paramKey);
        } catch (Exception e) {
            log.error("清除参数所有缓存失败: {}", paramKey, e);
        }
    }

    @Override
    public void clearAllCache() {
        try {
            // 清除所有参数定义缓存
            Set<String> defKeys = redisTemplate.keys(PARAM_DEFINITION_PREFIX + "*");
            if (!CollectionUtils.isEmpty(defKeys)) {
                redisTemplate.delete(defKeys);
            }
            
            // 清除所有参数值缓存
            Set<String> valKeys = redisTemplate.keys(PARAM_VALUE_PREFIX + "*");
            if (!CollectionUtils.isEmpty(valKeys)) {
                redisTemplate.delete(valKeys);
            }
            
            // 清除缓存统计
            redisTemplate.delete(CACHE_STATS_KEY);
            
            log.info("清除所有参数缓存完成");
        } catch (Exception e) {
            log.error("清除所有参数缓存失败", e);
        }
    }

    @Override
    public void warmUpCache() {
        try {
            log.info("开始预热参数缓存...");
            
            // TODO: 根据实际业务需求实现预热逻辑
            // 例如：加载最常用的参数、系统参数等
            
            // 示例：加载所有系统参数
            // List<ParamDefinitionVO> systemParams = paramDefinitionService.getSystemParams();
            // Map<String, ParamDefinitionVO> cacheData = systemParams.stream()
            //         .collect(Collectors.toMap(ParamDefinitionVO::getParamKey, Function.identity()));
            // batchCacheParamDefinitions(cacheData);
            
            log.info("参数缓存预热完成");
        } catch (Exception e) {
            log.error("参数缓存预热失败", e);
        }
    }

    @Override
    public Map<String, Object> getCacheStatistics() {
        try {
            Map<Object, Object> stats = redisTemplate.opsForHash().entries(CACHE_STATS_KEY);
            
            Map<String, Object> result = new HashMap<>();
            for (Map.Entry<Object, Object> entry : stats.entrySet()) {
                result.put(entry.getKey().toString(), entry.getValue());
            }
            
            // 计算命中率
            long defHit = getLongValue(result, "definition_cache_hit");
            long defMiss = getLongValue(result, "definition_cache_miss");
            long valHit = getLongValue(result, "value_cache_hit");
            long valMiss = getLongValue(result, "value_cache_miss");
            
            if ((defHit + defMiss) > 0) {
                double defHitRate = (double) defHit / (defHit + defMiss) * 100;
                result.put("definition_hit_rate", String.format("%.2f%%", defHitRate));
            }
            
            if ((valHit + valMiss) > 0) {
                double valHitRate = (double) valHit / (valHit + valMiss) * 100;
                result.put("value_hit_rate", String.format("%.2f%%", valHitRate));
            }
            
            result.put("last_update", new Date());
            return result;
        } catch (Exception e) {
            log.error("获取缓存统计失败", e);
            return new HashMap<>();
        }
    }

    @Override
    public boolean isHealthy() {
        try {
            // 简单的健康检查：尝试设置和获取一个测试键
            String testKey = "param:health:check";
            String testValue = "ok";
            
            redisTemplate.opsForValue().set(testKey, testValue, 10, TimeUnit.SECONDS);
            Object result = redisTemplate.opsForValue().get(testKey);
            
            boolean healthy = testValue.equals(result);
            if (healthy) {
                redisTemplate.delete(testKey);
            }
            
            return healthy;
        } catch (Exception e) {
            log.error("缓存健康检查失败", e);
            return false;
        }
    }

    // ===== 私有辅助方法 =====

    private String buildValueCacheKey(String paramKey, String environment) {
        return PARAM_VALUE_PREFIX + paramKey + ":" + environment;
    }

    private void updateCacheStats(String statKey, long increment) {
        try {
            redisTemplate.opsForHash().increment(CACHE_STATS_KEY, statKey, increment);
            redisTemplate.expire(CACHE_STATS_KEY, STATS_TTL, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.debug("更新缓存统计失败: {}", statKey, e);
        }
    }

    private long getLongValue(Map<String, Object> map, String key) {
        Object value = map.get(key);
        if (value instanceof Number) {
            return ((Number) value).longValue();
        }
        return 0L;
    }
}
